#include "clog.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

typedef struct
{
    struct tm tm_info;
    long milliseconds;
} time_with_ms;

static string lv_str[MAX_INDEX] = {"DEBUG", "INFO", "ERROR"};

inline void get_current_time_with_ms(string &date, string &time)
{
    struct timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);

    struct tm local_tm;
    localtime_r(&ts.tv_sec, &local_tm);
    long milliseconds = ts.tv_nsec / 1000000L;

    char date_str[64] = "";
    sprintf(date_str, "%04d-%02d-%02d",
            local_tm.tm_year + 1900,
            local_tm.tm_mon + 1,
            local_tm.tm_mday);
    date = date_str;

    char time_str[64] = "";
    sprintf(time_str, "%02d:%02d:%02d.%03ld",
            local_tm.tm_hour,
            local_tm.tm_min,
            local_tm.tm_sec, milliseconds);
    time = time_str;

    return;
}

Clog::Clog() : level(0), running(0), filename(), fp(nullptr), queue(8, true) {};

Clog::~Clog()
{
    return;
}

void Clog::write_log(string str, int level)
{
    if (level < this->level)
        return;
    string date = "", time = "";
    LogData logData;

    get_current_time_with_ms(date, time);

    logData.date = date;
    logData.level = level;
    logData.data = date + " " + time + " " + lv_str[level] + " " + str;
    this->queue.set_data(logData);
    return;
}

void Clog::recv_thread()
{
    while (1)
    {
        if (this->queue.get_size() == 0)
        {
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
        else
        {
            LogData logData;
            int ret = this->queue.get_data(logData);
            if (ret != 0)
            {
                fprintf(stderr, "get_data failed, this is bug!\n");
                continue;
            }
            if (logData.date != this->today)
            {
                today = logData.date;
                if (fp)
                {
                    fclose(fp);
                    fp = nullptr;
                }
#ifdef _WIN32
                if (this->path.length()!=0 && this->path.at(path.size() - 1) != '\\')
                    path.append("\\");
#else
                if (this->path.length()!=0 && this->path.at(path.size() - 1) != '/')
                    path.append("/");
#endif
                string fullname = path + today + "_" + filename + ".txt";
                fp = fopen(fullname.c_str(), "w");
                if (!fp)
                {
                    fprintf(stderr, "fopen error:%s\n", strerror(errno));
                    return;
                }
            }
            if (fp)
            {
                fprintf(fp, "%s", logData.data.c_str());
                fflush(fp);  // 可优化，立即刷新或者延迟刷新。本demo选择立即刷新
            }
#if 0
            printf("have data:%s--%d\n", logData.data.c_str(), ret);
            if (logData.data.length() == 0) // test
            {
                _exit(0);
            }
#endif
        }
    }
}

void Clog::start_thread()
{
    if (this->running == 1)
        return;
    this->running = 1;
    std::thread tid = std::thread(&Clog::recv_thread, this);
    tid.detach();
    return;
}